using System.Collections.Generic;
using System.Linq;

using UnityEngine;

using Gamelogic.Extensions;

namespace Gamelogic.Extensions
{ 
    public static class Vector3Extensions
    {
        public static Vector3 PerpXY(this Vector3 v)
        {
            return new Vector3(-v.y, v.x, v.z);
        }

        public static void TrimParallel(this List<Vector3> dao0Points)
        {
            for (int i = dao0Points.Count - 2; i >= 1; i--)
            {
                var v0 = dao0Points[i + 1] - dao0Points[i];
                var v1 = dao0Points[i] - dao0Points[i - 1];
                if (Vector3.Cross(v0.normalized, v1.normalized).sqrMagnitude <= 0.001f)
                {
                    dao0Points.RemoveAt(i);
                }
            }
        }
    }

    public static class GLMathf
    {
        public static float FloorMod(float a, float b)
        {
            float result = a % b;
            return (result < 0) ? (result + Mathf.Abs(b)) : result;
        }
    }

}

public class LineMeshBuilder : MeshBuilder
{
   public List<Vector3> points;
   public bool loop;

   [Min(0)]
   public float width;

   override protected List<Vector3> CalculateVertices()
   {
      float halfWidth = width / 2f;

      var topLines = new List<Line>();
      var bottomLines = new List<Line>();

      int segmentCount = loop ? points.Count : (points.Count - 1);

      Debug.Log(segmentCount);

      for (int i = 0; i < segmentCount; i++)
      {
         int j = (i == points.Count - 1) ? 0 : i + 1;

         var direction = (points[j] - points[i]).normalized;

         meshDebug.AddArrow(points[i], direction, GLColor.Green);

         var left = direction.PerpXY();
         var top = points[i] + halfWidth * left;
         var bottom = points[i] - halfWidth * left;

         topLines.Add(new Line { offset = top, direction = direction });
         bottomLines.Add(new Line { offset = bottom, direction = direction });
      }

      var vertices = new List<Vector3>();

      for (int i = 0; i < points.Count; i++)
      {
         int j = (i == 0) ? points.Count - 1 : i - 1;

         if ((i == 0) && !loop)
         {
            vertices.Add2(topLines[i].offset, bottomLines[i].offset);
         }
         else if ((i == points.Count - 1) && !loop)
         {
            var direction = topLines[j].direction;

            var left = direction.PerpXY();
            var top = points[i] + halfWidth * left;
            var bottom = points[i] - halfWidth * left;

            vertices.Add2(top, bottom);
         }
         else if (MeshBuilderUtils.IsParallel(topLines[i], topLines[j]))
         {
            Debug.Log("Parallel");

            vertices.Add2(topLines[i].offset, bottomLines[i].offset);
         }
         else
         {
            var topIntersection = MeshBuilderUtils.GetIntersection(topLines[i], topLines[j]);
            var bottomIntersection = MeshBuilderUtils.GetIntersection(bottomLines[i], bottomLines[j]);

            meshDebug.AddDotXY(topIntersection, GLColor.Magenta);
            meshDebug.AddDotXY(bottomIntersection, GLColor.Green);

            vertices.Add2(topIntersection, bottomIntersection);
         }
      }

      return vertices;
   }

   override protected List<int> CalculateTriangles()
   {
      int segmentCount = loop ? points.Count : points.Count - 1;
      var triangles = new List<int>();

      for (int i = 0; i < segmentCount; i++)
      {
         int j = (i == points.Count - 1) ? 0 : i + 1;

         triangles.AddRange(
            MeshBuilderUtils.QuadTriangles(
               2 * i + 0,
               2 * i + 1,
               2 * j + 1,
               2 * j + 0
            )
         );
      }

      return triangles;
   }

   override protected List<Vector2> CalculateUvs(List<Vector3> vertices)
   {
      var lengths = points.Differences((u, v) => (v - u).magnitude, loop);
      float totalLength = lengths.Sum();

      int segmentCount = loop ? points.Count : points.Count - 1;

      var uvs = new List<Vector2>();
      uvs.Add2(Vector2.zero, Vector2.up);
      
      float accumulativeLength = 0;

      for(int i = 0; i < points.Count - 1; i++)
      {
         accumulativeLength += lengths[i];
         float u = accumulativeLength / totalLength;

         uvs.Add2(new Vector2(u, 0), new Vector2(u, 1));
      }

      return uvs;
   }
}